#include "role.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include <anet/util.h>

const static char* STATUS_IDLE_STR = "idle";
const static char* STATUS_PASS_STR = "pass";
const static char* STATUS_ATTACK_STR = "attack";
const static char* STATUS_CRITICAL_ATTACK_STR = "critical attack";
const static char* STATUS_DEFEND_STR = "defend";
const static char* STATUS_DODGE_STR  = "dodge";

const char* GetStatusStr(int status) {
    switch (status) {
        case STATUS_IDLE: return STATUS_IDLE_STR;
        case STATUS_PASS: return STATUS_PASS_STR;
        case STATUS_ATTACK: return STATUS_ATTACK_STR;
        case STATUS_CRITICAL_ATTACK: return STATUS_CRITICAL_ATTACK_STR;
        case STATUS_DEFEND: return STATUS_DEFEND_STR;
        case STATUS_DODGE: return STATUS_DODGE_STR;
    }
    return NULL;
}

Role* RoleCreate(const char* group) {
    Role* role = (Role *)malloc(sizeof(Role));
    role->group  = strdup(group);
    role->who    = ROLE_NONE;
    role->target = NULL;
    return role;
}

void RoleDestroy(Role* role) {
    if (role) {
        switch (role->who) {
            case ROLE_HERO: HeroDestroy(role->target);break;
            case ROLE_SWORD: SwordDestroy(role->target);break;
            case ROLE_BOSS: BossDestroy(role->target);break;
        }
        free(role->group);
        free(role);
    }
}

Hero* HeroCreate(double max_hp, double hp_recover_rate, double max_mp, double mp_recover_rate, double mp_consumption,
        double attack, double critical_multiply, double critical_rate) {
    Hero* hero = (Hero *)malloc(sizeof(Hero));
    hero->max_hp  = max_hp;
    hero->max_mp  = max_mp;
    hero->hp = max_hp;
    hero->mp = max_mp;
    hero->hp_recover_rate = hp_recover_rate;
    hero->mp_recover_rate = mp_recover_rate;
    hero->status = STATUS_IDLE; 

    hero->mp_consumption    = mp_consumption;
    hero->attack            = attack;
    hero->critical_multiply = critical_multiply;
    hero->critical_rate     = critical_rate;

    return hero;
}

void HeroDestroy(Hero* hero) {
    if (hero) {
        free(hero);
    }
}

void HeroReset(Hero* hero, Boss* boss) {
    hero->hp += (hero->hp_recover_rate * boss->attack);
    hero->mp += (hero->mp_recover_rate * hero->mp_consumption);
    hero->hp = hero->hp > hero->max_hp ? hero->max_hp : hero->hp;
    hero->mp = hero->mp > hero->max_mp ? hero->max_mp : hero->mp;
    hero->status = STATUS_IDLE;
}

static void _HeroAttack(Hero* hero, Sword* sword, Boss* boss, double power) {
  //    currentAction = "attack"
  //
  //        var heroRate: Float = power
  //        var swordRate: Float = power
  //        if (boss.currentAction == "defend") {
  //            heroRate = 0.8
  //                swordRate = 1.2
  //        }
  //    if (Float(arc4random_uniform(100))/100.0 < CRITICAL_HAPPEN_RATE) {
  //        heroRate *= CRITICAL_MULTIPLY
  //            swordRate *= CRITICAL_MULTIPLY
  //    }
  //    Mp -= MP_CONSUMPTION
  //
  //        if (sword.currentAction != "dodge") {
  //            boss.Hp -= (HERO_ATTACK * heroRate)
  //                sword.Hp -= (SWORD_DAMAGE * swordRate)
  //        }

  double heroRate  = power;
  double swordRate = power;
  if (boss->status == STATUS_DEFEND) {
    heroRate  = 0.8;
    swordRate = 1.2;
  }

  int flag = 0;
  if ( 1.0 * rand() / RAND_MAX < hero->critical_rate) {
    heroRate  *= hero->critical_multiply;
    swordRate *= hero->critical_multiply;
    flag = 1;
  }
  hero->mp -= hero->mp_consumption;

  if (sword->status != STATUS_DODGE) {
    boss->hp  -= (hero->attack + sword->attack) * heroRate;
    sword->hp -= (sword->damage * swordRate);
  }

  if (flag) {
    hero->status = STATUS_CRITICAL_ATTACK;
  } else {
    hero->status = STATUS_ATTACK;
  }
}

void HeroAttack(Hero* hero, Sword* sword, Boss* boss) {
    _HeroAttack(hero, sword, boss, 1);
}

void HeroCriticalAttack(Hero* hero, Sword* sword, Boss* boss) {
    hero->hp -= 10;
    _HeroAttack(hero, sword, boss, hero->critical_multiply);
    hero->status = STATUS_CRITICAL_ATTACK;
}

void HeroPass(Hero* hero) {
    hero->status = STATUS_PASS;
}

void HeroStates(Hero* hero) {
    const char* st = GetStatusStr(hero->status); 
    LogInfo("hero => HP: %lf/%lf, MP: %lf/%lf, STATUS: %s", hero->hp, hero->max_hp, hero->mp, hero->max_mp, st);
}

int HeroBufferSize(Hero* hero) {
   return 10 * 8 + 4; 
}

int HeroPrint(Hero* hero, char* bf) { 
    char* b = bf;
    *(double *)b = hero->max_hp;
    b += sizeof(double);
    *(double *)b = hero->max_mp;
    b += sizeof(double);

    *(double *)b = hero->hp;
    b += sizeof(double); 

    *(double *)b = hero->mp;
    b += sizeof(double);

    *(double *)b = hero->hp_recover_rate;
    b += sizeof(double);

    *(double *)b = hero->mp_recover_rate;
    b += sizeof(double);

    *(int *)b = hero->status;
    b += sizeof(int);

    *(double *)b = hero->mp_consumption;
    b += sizeof(double);

    *(double *)b = hero->attack;
    b += sizeof(double);

    *(double *)b = hero->critical_multiply;
    b += sizeof(double);

    *(double *)b = hero->critical_rate;
    b += sizeof(double);

    assert(b - bf == HeroBufferSize(hero));
    return b - bf;
}

Boss* BossCreate(double max_hp, double hp_recover_rate, double max_mp, double mp_recover_rate, double mp_consumption,
        double attack, double critical_multiply, double critical_rate) {
    Boss* boss = (Boss *)malloc(sizeof(Boss));
    boss->max_hp  = max_hp;
    boss->max_mp  = max_mp;
    boss->hp = max_hp;
    boss->mp = max_mp;
    boss->hp_recover_rate = hp_recover_rate;
    boss->mp_recover_rate = mp_recover_rate;
    boss->status = STATUS_IDLE; 

    boss->mp_consumption    = mp_consumption;
    boss->attack            = attack;
    boss->critical_multiply = critical_multiply;
    boss->critical_rate     = critical_rate;

    return boss;
}

void BossDestroy(Boss* boss) {
    if (boss) {
        free(boss);
    }
}

void BossReset(Boss* boss, Hero* hero) {
  boss->hp += (boss->hp_recover_rate * hero->attack);
  boss->mp += (boss->mp_recover_rate * boss->mp_consumption);
  boss->hp = boss->hp > boss->max_hp ? boss->max_hp : boss->hp;
  boss->mp = boss->mp > boss->max_mp ? boss->max_mp : boss->mp;
  boss->status = STATUS_IDLE;
}

void BossAttack(Boss* boss, Hero* hero, Sword* sword) {
  //    currentAction = "attack"
  //
  //        Mp -= MP_CONSUMPTION
  //
  //        var rate: Float = 1
  //        if (Float(arc4random_uniform(100))/100.0 < CRITICAL_HAPPEN_RATE) {
  //            rate = CRITICAL_MULTIPLY
  //        }
  //    hero.Hp -= (BOSS_ATTACK * rate)

  boss->mp -= boss->mp_consumption;
  int flag = 0;
  double rate = 1;
  if (rand() / RAND_MAX < boss->critical_rate) {
    rate = boss->critical_rate;
    flag = 1;
  }
  boss->hp -= (boss->attack * rate);

  if (flag) {
    boss->status = STATUS_CRITICAL_ATTACK;
  } else {
    boss->status = STATUS_ATTACK;
  }
}

void BossDefend(Boss* boss) {
    boss->status = STATUS_DEFEND;
}

void BossPass(Boss* boss) {
    boss->status = STATUS_PASS;
}

void BossStates(Boss* boss) {
    const char* st = GetStatusStr(boss->status); 
    LogInfo("boss => HP: %lf/%lf, MP: %lf/%lf, STATUS: %s", boss->hp, boss->max_hp, boss->mp, boss->max_mp, st);
}

int BossBufferSize(Boss* boss) {
   return 10 * 8 + 4; 
}

int BossPrint(Boss* boss, char* bf) { 
    char* b = bf;
    *(double *)b = boss->max_hp;
    b += sizeof(double);
    *(double *)b = boss->max_mp;
    b += sizeof(double);

    *(double *)b = boss->hp;
    b += sizeof(double); 

    *(double *)b = boss->mp;
    b += sizeof(double);

    *(double *)b = boss->hp_recover_rate;
    b += sizeof(double);

    *(double *)b = boss->mp_recover_rate;
    b += sizeof(double);

    *(int *)b = boss->status;
    b += sizeof(int);

    *(double *)b = boss->mp_consumption;
    b += sizeof(double);

    *(double *)b = boss->attack;
    b += sizeof(double);

    *(double *)b = boss->critical_multiply;
    b += sizeof(double);

    *(double *)b = boss->critical_rate;
    b += sizeof(double);

    assert(b - bf == BossBufferSize(boss));
    return b - bf;
}

Sword* SwordCreate(double max_hp, double hp_recover_rate, double max_mp, double mp_recover_rate, double mp_consumption,
        double attack, double damage) {
    Sword* sword = (Sword *)malloc(sizeof(Sword));
    sword->max_hp  = max_hp;
    sword->max_mp  = max_mp;
    sword->hp = max_hp;
    sword->mp = max_mp;
    sword->hp_recover_rate = hp_recover_rate;
    sword->mp_recover_rate = mp_recover_rate;
    sword->status = STATUS_IDLE; 

    sword->mp_consumption    = mp_consumption;
    sword->attack            = attack;

    sword->damage            = damage;

    return sword;
}

void SwordDestroy(Sword* sword) {
    if (sword) {
        free(sword);
    }
}

void SwordReset(Sword* sword) {
    sword->hp += (sword->hp_recover_rate * sword->damage);
    sword->mp += (sword->mp_recover_rate * sword->mp_consumption);
    sword->hp = sword->hp > sword->max_hp ? sword->max_hp : sword->hp;
    sword->mp = sword->mp > sword->max_mp ? sword->max_mp : sword->mp;
    sword->status = STATUS_IDLE;
}

void SwordDodge(Sword* sword, Boss* boss) {
//    currentAction = "dodge"
//
//        Mp -= MP_CONSUMPTION
//        if (boss.currentAction == "defend") {
//            Hp -= SWORD_DAMAGE
//        }
    sword->mp -= sword->mp_consumption;
    if (boss->status == STATUS_DEFEND) {
        sword->hp -= sword->damage;
    }
    sword->status = STATUS_DODGE;
}

void SwordPass(Sword* sword) {
    sword->status = STATUS_PASS;
}

void SwordStates(Sword* sword) {
    const char* st = GetStatusStr(sword->status); 
    LogInfo("sword => HP: %lf/%lf, MP: %lf/%lf, STATUS: %s", sword->hp, sword->max_hp, sword->mp, sword->max_mp, st);
}

int SwordBufferSize(Sword* sword) {
    return 9 * 8 + 4;
}

int SwordPrint(Sword* sword, char* bf) {
    char* b = bf;

    *(double *)b = sword->max_hp;
    b += sizeof(double);

    *(double *)b = sword->max_mp;
    b += sizeof(double);

    *(double *)b = sword->hp;
    b += sizeof(double);

    *(double *)b = sword->mp;
    b += sizeof(double);

    *(double *)b = sword->hp_recover_rate;
    b += sizeof(double);

    *(double *)b = sword->mp_recover_rate;
    b += sizeof(double);

    *(int *)b = sword->status;
    b += sizeof(int);

    *(double *)b = sword->mp_consumption;
    b += sizeof(double);

    *(double *)b = sword->attack;
    b += sizeof(double);

    *(double *)b = sword->damage;
    b += sizeof(double);

    assert(b - bf == SwordBufferSize(sword));
    return b - bf;
}

